package com.mlethe.library.fingerprint;

import android.content.Context;
import android.content.DialogInterface;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
import android.os.CancellationSignal;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.mlethe.library.fingerprint.callback.FingerprintCallback;
import com.mlethe.library.fingerprint.callback.IFingerprint;
import com.mlethe.library.fingerprint.entity.VerificationDialogStyle;
import com.mlethe.library.fingerprint.uitls.CipherHelper;
import com.mlethe.library.fingerprint.uitls.FingerprintUtil;

import javax.crypto.Cipher;

/**
 * Android P == 9.0
 *
 * @author Mlethe
 * @date 2019/7/9
 */
@RequiresApi(api = Build.VERSION_CODES.P)
class FingerprintImplForAndroidP implements IFingerprint {

    private static final String SECRET_MESSAGE = "Very secret message";

    /**
     * 指纹加密
     */
    private BiometricPrompt.CryptoObject cryptoObject;

    /**
     * 用于取消扫描器的扫描动作
     */
    private CancellationSignal cancellationSignal;

    /**
     * 指向调用者的指纹回调
     */
    private FingerprintCallback fingerprintCallback;

    /**
     * 弹窗样式
     */
    private VerificationDialogStyle verificationDialogStyle;

    private static final class Holder {
        private static final FingerprintImplForAndroidP INSTANCE = new FingerprintImplForAndroidP();
    }

    private FingerprintImplForAndroidP() {
    }

    public static FingerprintImplForAndroidP getInstance() {
        return Holder.INSTANCE;
    }

    /**
     * 创建指纹加密
     */
    @Override
    public boolean createCryptoObject(boolean isSetting) {
        // 指纹加密，提前进行Cipher初始化，防止指纹认证时还没有初始化完成
        try {
            if (cryptoObject == null) {
                CipherHelper cipherHelper = CipherHelper.getInstance();
                Cipher cipher = cipherHelper.createCipher();
                if (isSetting) {
                    cipherHelper.createKey();
                }
                if (cipherHelper.initCipher(cipher)) {
                    return true;
                }
                if (isSetting) {
                    cipherHelper.createKey();
                }
                cryptoObject = new BiometricPrompt.CryptoObject(cipher);
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 设置回调监听
     *
     * @param callback
     */
    @Override
    public void setFingerprintCallback(FingerprintCallback callback) {
        fingerprintCallback = callback;
    }

    /**
     * 设置弹窗样式
     *
     * @param verificationDialogStyle
     */
    @Override
    public void setVerificationDialogStyle(VerificationDialogStyle verificationDialogStyle) {
        this.verificationDialogStyle = verificationDialogStyle;
    }

    /**
     * 调起指纹验证
     *
     * @param context
     */
    @Override
    public void authenticate(@NonNull Context context) {
        if (verificationDialogStyle == null) {
            return;
        }
        Context appContext = context.getApplicationContext();
        /*
         * 初始化 BiometricPrompt.Builder
         */
        String title = TextUtils.isEmpty(verificationDialogStyle.getTitle()) ?
                "指纹验证" : verificationDialogStyle.getTitle();
        String cancelText = TextUtils.isEmpty(verificationDialogStyle.getCancelBtnText()) ?
                appContext.getString(R.string.biometricprompt_cancel) :
                verificationDialogStyle.getCancelBtnText();
        BiometricPrompt.Builder builder = new BiometricPrompt.Builder(appContext)
                .setTitle(title)
                .setNegativeButton(cancelText, appContext.getMainExecutor(), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        cancel();
                    }
                });
        if (!TextUtils.isEmpty(verificationDialogStyle.getSubTitle())) {
            builder.setSubtitle(verificationDialogStyle.getSubTitle());
        }
        if (!TextUtils.isEmpty(verificationDialogStyle.getDescription())) {
            builder.setDescription(verificationDialogStyle.getDescription());
        }

        // 构建 BiometricPrompt
        BiometricPrompt biometricPrompt = builder.build();

        // 取消扫描，每次取消后需要重新创建新示例
        cancellationSignal = new CancellationSignal();
        // 认证结果回调
        BiometricPrompt.AuthenticationCallback authenticationCallback = new BiometricPrompt.AuthenticationCallback() {

            @Override
            public void onAuthenticationError(int errorCode, CharSequence errString) {
                super.onAuthenticationError(errorCode, errString);
                if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE ||
                        errorCode == BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT) {
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onHwUnavailable();
                    }
                } else if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_NO_BIOMETRICS) {
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onNoneEnrolled();
                    }
                } else if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED ||
                        errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED) {
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onCancel();
                    }
                } else {
                    if (fingerprintCallback != null) {
                        fingerprintCallback.onError(errString.toString());
                    }
                }
                onDestroy();
            }

            @Override
            public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
                super.onAuthenticationHelp(helpCode, helpString);
                if (fingerprintCallback != null) {
                    fingerprintCallback.onFailed(helpString.toString());
                }
            }

            @Override
            public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
                super.onAuthenticationSucceeded(result);
                Cipher cipher = result.getCryptoObject().getCipher();
                if (cipher != null) {
                    try {
                        /*
                         * 用于检测三星手机指纹库变化，
                         * 三星手机指纹库发生变化后前面的initCipher检测不到KeyPermanentlyInvalidatedException
                         * 但是cipher.doFinal(SECRET_MESSAGE.getBytes())会抛出异常
                         * 因此以此监听三星手机的指纹库变化
                         */
                        //针对三星手机，开启了监听才去检测设备指纹库变化
                        if (Build.MANUFACTURER.equalsIgnoreCase("samsung")) {
                            cipher.doFinal(SECRET_MESSAGE.getBytes());
                        }
                        if (fingerprintCallback != null) {
                            fingerprintCallback.onSucceeded();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        if (fingerprintCallback != null) {
                            fingerprintCallback.onChange();
                        }
                    }
                }

                onDestroy();
            }

            @Override
            public void onAuthenticationFailed() {
                super.onAuthenticationFailed();
                if (fingerprintCallback != null) {
                    fingerprintCallback.onFailed("验证失败");
                }
            }
        };
        /*
         * 拉起指纹验证模块，等待验证
         * Executor：
         * context.getMainExecutor()
         */
        biometricPrompt.authenticate(cryptoObject, cancellationSignal, appContext.getMainExecutor(), authenticationCallback);
    }

    /**
     * 在 Android Q，Google 提供了 Api BiometricManager.canAuthenticate() 用来检测指纹识别硬件是否可用及是否添加指纹
     * 不过尚未开放，标记为"Stub"(存根)
     * 所以暂时还是需要使用 Andorid 6.0 的 Api 进行判断
     */
    @Override
    public boolean canAuthenticate(Context context) {
        /*
         * 硬件是否支持指纹识别
         */
        if (!FingerprintUtil.isHardwareDetected(context)) {
            if (fingerprintCallback != null) {
                fingerprintCallback.onHwUnavailable();
            }
            onDestroy();
            return false;
        }
        /*
         * 是否已添加指纹
         */
        if (!FingerprintUtil.hasEnrolledFingerprints(context)) {
            if (fingerprintCallback != null) {
                fingerprintCallback.onNoneEnrolled();
            }
            onDestroy();
            return false;
        }
        return true;
    }

    @Override
    public void cancel() {
        if (cancellationSignal != null && !cancellationSignal.isCanceled()) {
            cancellationSignal.cancel();
            if (fingerprintCallback != null) {
                fingerprintCallback.onCancel();
            }
        }
        onDestroy();
    }

    @Override
    public void onDestroy() {
        cryptoObject = null;
        fingerprintCallback = null;
        cancellationSignal = null;
        verificationDialogStyle = null;
    }
}